#include "message.h"
#include "serial.h"
#include "iap.h"
#include "bsp/cpu_driver.h"
#include "utils/logger.h"
#include "utils/utils.h"
#include "os/os.h"

static void message_call_back(head_t id, uint8_t *data, int len);
static void free_message(message_t *message);
static void message_process(message_t *message);
#define MAX_CAN_MESSAGE 10
static message_t messages[MAX_CAN_MESSAGE];

#if CONFIG_BOARD_COM_UART
static serial_t m_serial;
void handle_serial_frame(uint8_t *data, int len) {
	head_t can_id;
    data += 1;
    len  -= 1;
	memcpy(&can_id, data, 4);
	message_call_back(can_id, data+4, len-4);
}
#endif

#if CONFIG_BOARD_COM_CAN
void handle_can_frame(u32 id, u8 *data, int len) {
	head_t can_id;
	can_id.id = id;
	message_call_back(can_id, data, len);
}
#endif
void message_init(void) {
#if CONFIG_BOARD_COM_UART
    m_serial.hw_id = CONFIG_HW_ID_UART0;
    m_serial.rate = 500000;
    m_serial.rx_handler = handle_serial_frame;
    serial_init(&m_serial);
#endif
#if CONFIG_BOARD_COM_CAN
	can_drv_init(handle_can_frame);
#endif
}

static message_t *get_message_by_id(head_t id){
	message_t *idle_msg = NULL;
	message_t *src_msg = NULL;
	for (int i = 0; i < MAX_CAN_MESSAGE; i++){
		/*first found the same src&dest */
		if ((messages[i].src == id.src) && (messages[i].dest == id.dest)){
			return (messages + i);
		}
		if (messages[i].src == id.src){
			src_msg = messages + i;
		}else if (messages[i].data == NULL){
			idle_msg = messages + i;
		}
	}
	return ((src_msg!=NULL)?src_msg:idle_msg);
}

#if CONFIG_BOARD_COM_UART
int serial_send_message(serial_t *serial, u32 efid, u8 *buff, int len){

	head_t frame_id;
	u32 total_frames = ((len + 7) >> 3);
	int send_len = len;
	u32 id = 1;

	frame_id.id = efid;
	frame_id.total = total_frames;

	while(send_len > 0){
		frame_id.idx = id;
		int count = _min(8,send_len);
		frame_id.length = count;
		serial_send_frame(serial, frame_id.id, buff + (len - send_len), count);
		send_len -= count;
		id ++;
	}
	return len;
}
#endif

s32 message_send(uint32_t head, u8 *data, int len, s32 timeout){
#if CONFIG_BOARD_COM_CAN
	can_send_message(head, data, len);
#endif

#if CONFIG_BOARD_COM_UART
	head_t efid;
	efid.id = head;
	efid.total = efid.idx = 1;
	serial_send_message(&m_serial, efid.id, data, len);
#endif
	return 0;
}


static void message_call_back(head_t id, uint8_t *data, int len){
	/*sys_debug("id = %0x, data = ", id.id);
	for(int i = 0; i < len; i++) {
		sys_debug("0x%x ", data[i]);
	}
	sys_debug("\n");*/
	message_t *message = get_message_by_id(id);
	if (message == NULL) {
		return ;
	}
	uint16_t key = 0;

	if (len > 8) {
		key = decoder_key(data);
		len -= 2;
		message->key = key;
		message->dest = id.dest;
		message->src = id.src;
		message->type= id.type;
		message->total_frame = 1;
		message->idx = 1;
		message->len = len;
		if (len > 0) {
			message->data = os_alloc(len);
			memcpy(message->data, data+2, len);
		}else {
			message->data = NULL;
		}
		message_process(message);
		return;
	}

	if (id.dest != MY_MESSAGE_ADDR) {
		return;
	}
	int total = id.total?id.total:32;
	int idx = id.idx?id.idx:32;

	if((total <= MESSAGE_MAX_FRAMES) && (idx <= MESSAGE_MAX_FRAMES)
			&& (idx <= total)){
		if ((idx == 1) && (len >= 2)) { //first frame for message
			key = decoder_key(data);
			if (message->data) {
				os_free(message->data);
			}

			message->key = key;
			message->dest = id.dest;
			message->src = id.src;
			message->data = os_alloc(total * 8);
			message->len = 0;
			message->idx = idx;
			message->type= id.type;
			message->total_frame = total;
			len = len - 2; //skip key
			data = data + 2;
		}
		if (message->data){
			if ((message->idx == idx) && (message->total_frame == total)){
				memcpy(message->data + message->len, data, len);
				message->len += len;
				if (idx == total) { //last frame
					message_process(message);
				}
				message->idx = (idx + 1);
			}else {
				free_message(message);
			}
		}
	}
}

static void message_process(message_t *message){
	//sys_debug("can %x [%x -> %x], len = %d\n", message->key, message->src, message->dest, message->len);
	if ((message->key & 0xFF) >= 0xF0) {
#ifndef USE_MEX_CMD
		process_iap_message(message);
#endif
		if (message->data) {
			os_free(message->data);
		}
	}
	free_message(message);
}

void message_send_ack(message_t *msg, uint8_t success) {
	u8 response[3];
	encoder_key(response, msg->key);
	response[2] = success;
	message_send(response_head(msg->src), response, 3, 100);
}
void message_send_ack2(uint8_t addr, uint16_t key, uint8_t data) {
	message_t message;
	message.src = addr;
	message.key = key;
    message_send_ack(&message, data);
}
void message_send_response(uint8_t addr, uint8_t *data, int len) {
    message_send( response_head(addr), data, len, 100);
}

void message_send_indicator(uint8_t addr, uint8_t *data, int len) {
    message_send( indicator_head(addr), data, len, 100);
}

void message_send_request(uint8_t addr, uint8_t *data, int len) {
    message_send(request_head(addr, 1), data, len, 100);
}

static void free_message(message_t *message){
	message->data = NULL;
	message->src = 0;
	message->len = 0;
	message->idx = 0xFF;
	message->total_frame = 0xFF;
}
